#include "ocrthread.h"

#include "processing.h"

#include <QFile>
#include <QIODevice>
#include <QCryptographicHash>
#include <QByteArray>
#include <QDebug>
#include <QDir>
#include <QRandomGenerator>
#include <QFileInfo>
#include <QVector>
#include <QPixmap>
#include <QImage>
#include <opencv2/opencv.hpp>
#include "yolov8infer/yolov8infer.h"
#include "pubdef.h"
#include <QPainter>


#define CHECK_CANCEL if(checkCanceled())return;
static int ocrId = 1;
OCRThread::OCRThread()
{
    m_ocrId = ocrId++;
    setTerminationEnabled(true);
#ifdef QT_DEBUG
    printf("create OCRThread: %d\n", m_ocrId);
#endif
}


OCRThread::~OCRThread()
{
#ifdef QT_DEBUG
    printf("delete OCRThread: %d\n", m_ocrId);
#endif
}
void OCRThread::run()
{
#ifdef QT_DEBUG
    printf("begin run OCRThread: %d\n", m_ocrId);
#endif
    ocrImage();
#ifdef QT_DEBUG
    printf("end run OCRThread: %d\n", m_ocrId);
#endif
    if(!m_isCancel)
    {
        emit returnResult(0);
    }
}
QList<TextBox> &OCRThread::cutBoxSets()
{
    return m_cutBoxSet;
}
QList<int> &OCRThread::lineSets()
{
    return m_line_Set;
}
QList<int> &OCRThread::lineTopSets()
{
    return m_line_top_Set;
}

QPixmap &OCRThread::outImage()
{
    return m_img;
}
void OCRThread::setInputImage(QString path, QPixmap *img)
{
    m_srcPath = path;
    m_pSrcImg = img;
    m_cutBoxSet.clear();
}
bool ocrLock = false;
void OCRThread::setCancel()
{
    m_isCancel = true;
}



bool OCRThread::checkCanceled()
{
    if(m_isCancel || !m_isRunning)
    {
#ifdef QT_DEBUG
        //        printf("OCRThread Canceled: %d\n", m_ocrId);
#endif
        ocrLock = false;
        m_isRunning = false;
        return true;
    }
    return false;
}
QString OCRThread::loadCacheIfPossible()
{
    QString key;
    if(!m_isPixmap)
    {
        QFile file(m_srcPath);
        QString loadFilePath;

        if(file.open(QIODevice::ReadOnly)){

            qint64 size = file.size();
            QByteArray ba = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Md5);

            file.close();
            key = QString("%1+%2+%3").arg(OCR_VERSION).arg(size).arg(QString(ba.toHex()));

            loadFilePath = _settings->value(key, "").toString();
            printf("load Cache:%s,%s\n",qUtf8Printable(key), qUtf8Printable(loadFilePath));
            if(QFile::exists(loadFilePath))
            {
                QFile inFile(loadFilePath);
                if (inFile.open(QIODevice::ReadOnly)) {
                    printf("begin inStream\n");
                    QDataStream inStream(&inFile);
                    inStream >> m_cutBoxSet >> m_line_Set >> m_line_top_Set >> m_detSet;
                    inFile.close();


                    highlightText(m_cutBoxSet);
                    objDetAndClasFromCache();

                    m_isRunning = false;
                    ocrLock = false;
                    return key;
                }
            }
        }
        else
        {
#ifdef QT_DEBUG
            qDebug() << "打开文件失败";
#endif
            ocrLock = false;
            m_isRunning = false;
            return key;
        }
    }
    return key;
}
std::vector<OCRPredictResult> OCRThread::ocrTask()
{
    if(m_isPixmap)
    {
        QImage qImage = m_pSrcImg->toImage();
        // 确保QImage格式为24位RGB格式
        qImage = qImage.convertToFormat(QImage::Format_RGB888);

        cv::Mat mat(qImage.height(), qImage.width(),
                    CV_8UC3,
                    const_cast<uchar*>(qImage.bits()),
                    static_cast<size_t>(qImage.bytesPerLine())
                    );

        return ocr_img(mat);

    }
    else
    {
        return ocr_file(m_srcPath.toStdString().c_str());
    }

}
void OCRThread::preProcessOcrResults(QList<TextBox> &results, std::vector<OCRPredictResult> &ocr_result)
{
    int minX, minY, maxX, maxY;
    for (int i = 0; i < ocr_result.size(); i++) {
        std::vector<std::vector<int>> boxes = ocr_result[i].box;
        if (boxes.size() > 1)
        {
            for (int n = 0; n < boxes.size(); n++) {
                if(n==0)
                {
                    minX = boxes[n][0];
                    minY = boxes[n][1];
                    maxX = boxes[n][0];
                    maxY = boxes[n][1];
                }
                else
                {
                    if(boxes[n][0]<minX)
                    {
                        minX = boxes[n][0];
                    }
                    else if(boxes[n][0]>maxX)
                    {
                        maxX = boxes[n][0];
                    }
                    if(boxes[n][1]<minY)
                    {
                        minY = boxes[n][1];
                    }
                    else if(boxes[n][1]>maxY)
                    {
                        maxY = boxes[n][1];
                    }
                }
            }
            results.append(TextBox(QRect(QPoint(minX,minY), QPoint(maxX,maxY)),QString::fromStdString(ocr_result[i].text), QVector<float>::fromStdVector(ocr_result[i].poslist) , QVector<float>::fromStdVector(ocr_result[i].widthlist)));
        }
    }


    qSort(results.begin(),results.end(),[](const TextBox &a, const TextBox &b){
        return a.rect.bottom() < b.rect.bottom();
    });
}

void OCRThread::processOcrResults(QList<TextBox> &results)
{
    int line = 1;
    int t2;
    printf("----------------------");
    while(results.count())
    {
        //        t1 = results.at(0).rect.top();
        t2 = results.at(0).rect.bottom()-LINE_TOLENRANCE;
        m_line_Set.append(t2);
        int temp_top = t2;
        QList<TextBox> lineSet;
        for(int i=0;i<results.count();i++)
        {
            if(results.at(i).rect.top()<t2)
            {
                if(results.at(i).rect.top()<temp_top)
                {
                    temp_top = results.at(i).rect.top();
                }
                lineSet.append(results.at(i));
                results.removeAt(i);
                i--;
            }
        }
        m_line_top_Set.append(temp_top);
        qSort(lineSet.begin(),lineSet.end(),[](const TextBox &a, const TextBox &b){
            return a.rect.left() < b.rect.left();
        });

        for(int i=0;i<lineSet.count();i++)
        {
            lineSet[i].line = line;
            lineSet[i].flag = 1;
            printf("%s ",lineSet[i].text.toStdString().c_str());
        }
        lineSet[lineSet.count()-1].flag = 2;
        m_cutBoxSet.append(lineSet);
        lineSet.clear();
        printf("\n");
        line++;

    }
}

void OCRThread::saveCacheOrIndex(QString &key)
{
    //#ifdef QT_DEBUG
    if(!m_isPixmap && !key.isEmpty())
    {
        QString filePath = randomSaveFile();

        QFile saveFile(filePath);
        if (saveFile.open(QIODevice::WriteOnly)) {
            QDataStream out(&saveFile);
            //            out.setVersion(QDataStream::Qt_5_10);
            out << m_cutBoxSet << m_line_Set << m_line_top_Set << m_detSet;

            saveFile.close();
            _settings->setValue(key, filePath);

        }
    }
    //#endif
}

void OCRThread::highlightText(QList<TextBox> &result)
{
    /*    QImage imageDeepen = processing::pictureCutDeepen(m_pSrcImg->toImage(), m_pSrcImg->size());   //给图片加上蒙层
    QImage image = m_pSrcImg->toImage(); */                                                    //无蒙层原图

    m_img = m_pSrcImg->copy();
    auto pixelRatio = m_pSrcImg->devicePixelRatio();
//    m_img = m_pSrcImg->copy(QRect(0,0,
//                    m_pSrcImg->width()*pixelRatio,m_pSrcImg->height()*pixelRatio));
    QPainter painter(&m_img);
    painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿
//    m_img.setDevicePixelRatio(painter.device().p);
//    printf("image widht:%d, height:%d", m_img.width(),m_img.height());
//    printf("image widhtMM:%d, heightM:%d", m_pSrcImg->width(),m_pSrcImg->height());
    //    QPainter painter(this);

    // 步骤1: 绘制原始图片
    painter.drawPixmap(0, 0, *m_pSrcImg);

    // 步骤2: 在整个图片上绘制半透明的黑色遮罩以调暗图片
    // 注意：调整遮罩的透明度以控制调暗的程度
    QColor semiTransparentBlack(0, 0, 0, 60); // 透明度设置为128，可以根据需要调整
    painter.fillRect(m_pSrcImg->rect(), semiTransparentBlack);

    // 步骤3: 在某一区域重新绘制原始图片内容，保持该区域亮度不变
    // 假设我们想保持的区域是一个矩形，这里指定矩形的位置和大小


    //    CHECK_CANCEL

    //    for (int i = 0; i < m_cutBoxSet.length(); i++) {
    //        for (int j = m_cutBoxSet.at(i).rect.top(); j < m_cutBoxSet.at(i).rect.bottom() ; j++) {
    //            for (int k = m_cutBoxSet.at(i).rect.left(); k < m_cutBoxSet.at(i).rect.right() ; k++) {
    //                QColor color(image.pixelColor(k, j));
    //                imageDeepen.setPixel(k, j, color.rgb());
    //            }
    //        }
    //    }
    painter.scale(1/pixelRatio, 1/pixelRatio);
    for (int i = 0; i < result.length(); i++) {
        //        QRect brightArea(50, 50, 100, 100); // 根据需要调整位置和大小
        painter.drawPixmap(result.at(i).rect, *m_pSrcImg, result.at(i).rect);
    }
    //    m_img = QPixmap::fromImage(imageDeepen);

}


//void drawTranslucentRectangle(cv::Mat &image, const cv::Rect &rect, double alpha) {
//    // 确保alpha值在有效范围内
//    alpha = std::min(std::max(alpha, 0.0), 1.0);

//    // 创建一个与原图同大小的全白图像
//    cv::Mat whiteOverlay(image.size(), image.type(), cv::Scalar(255, 255, 255));

//    // 创建一个与原图同大小、类型的图像，用于存放最终的绘制结果
//    cv::Mat result;

//    // 先将全白图像和原图按照指定的透明度混合，并将结果存放在result中
//    cv::addWeighted(whiteOverlay(rect), alpha, image(rect), 1.0 - alpha, 0.0, result);

//    // 将混合后的矩形区域复制回原图上
//    result.copyTo(image(rect));
//}
void OCRThread::objDetAndClasFromCache()
{
    int detections = m_detSet.size();
    std::cout << "Number of detections:" << detections << std::endl;
    if(detections == 0)
    {
        return;
    }
    if(m_cutBoxSet.count())
    {
    }
    else
    {
        m_img = m_pSrcImg->copy();
    }
    auto pixelRatio = m_pSrcImg->devicePixelRatio();
    QPainter painter(&m_img);
    painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿



    // 选择支持中文的字体
    QFont font;
    font.setFamily("微软雅黑"); // 设置字体为微软雅黑
    font.setPointSize(16); // 设置字体大小
    painter.setFont(font);


    //            painter.drawText(pixmap.rect(), Qt::AlignCenter, QStringLiteral("你好，世界！"));


    painter.scale(1/pixelRatio, 1/pixelRatio);

    for (int i = 0; i < detections; ++i)
    {
        DetBox detBox = m_detSet.at(i);


        QRect box = detBox.rect;


        painter.setPen(QPen(QColor(detBox.color.at(0), detBox.color.at(1), detBox.color.at(2)), 2)); // 设置画笔颜色和线宽
        painter.drawRect(detBox.rect); // 绘制矩形




        QString text = detBox.text;

        printf("det obj:%s\n",qUtf8Printable(text));
        // 使用QFontMetrics测量文本大小
        QFontMetrics metrics(font);
        int textWidth = metrics.horizontalAdvance(text); // 获取文本的宽度
        int textHeight = metrics.height(); // 获取文本的高度

        //            painter.drawText(rect, Qt::AlignLeft | Qt::AlignTop, text);
        // 设置画笔颜色和画刷
        // 设置透明度为127的白色（透明度范围是0到255，0完全透明，255完全不透明）
        QColor semiTransparentColor(255, 255, 255, 80);

        // 设置画笔：透明的白色，无边框
        QPen pen(semiTransparentColor);
        painter.setPen(pen);

        // 设置画刷：同样是透明的白色
        QBrush brush(semiTransparentColor);
        painter.setBrush(brush);

        // 绘制矩形，这里的参数是矩形的位置和大小
        painter.drawRect(QRect(box.x(), box.y(), textWidth, textHeight));

        painter.setPen(Qt::black); // 设置画笔颜色为黑色
        painter.drawText(box.x(),box.y()-5+textHeight, text);

    }
    // 完成绘图
    painter.end();
}
void OCRThread::objDetAndClas(QList<TextBox> &results)
{
    Yolov8Infer *infer = Yolov8Infer::getInstance();
    QImage qImage = m_pSrcImg->toImage();
    // 确保QImage格式为24位RGB格式
    qImage = qImage.convertToFormat(QImage::Format_RGB888);

    cv::Mat frame(qImage.height(), qImage.width(),
                  CV_8UC3,
                  const_cast<uchar*>(qImage.bits()),
                  static_cast<size_t>(qImage.bytesPerLine())
                  );
    std::vector<Detection> output = infer->runInference(frame);
    int detections = output.size();
    std::cout << "Number of detections:" << detections << std::endl;
    if(detections == 0)
    {
        return;
    }
    if(results.count())
    {
    }
    else
    {
        m_img = m_pSrcImg->copy();
    }
    auto pixelRatio = m_pSrcImg->devicePixelRatio();
    QPainter painter(&m_img);
    painter.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿



    // 选择支持中文的字体
    QFont font;
    font.setFamily("微软雅黑"); // 设置字体为微软雅黑
    font.setPointSize(16); // 设置字体大小
    painter.setFont(font);


    //            painter.drawText(pixmap.rect(), Qt::AlignCenter, QStringLiteral("你好，世界！"));



    painter.scale(1/pixelRatio, 1/pixelRatio);
    for (int i = 0; i < detections; ++i)
    {
        Detection detection = output[i];

        //box, color, classname

        cv::Rect box = detection.box;
        cv::Scalar color = detection.color;

        //            // Detection box
        //            cv::rectangle(frame, box, color, 2);

        painter.setPen(QPen(QColor(color[2], color[1], color[0]), 2)); // 设置画笔颜色和线宽
        painter.drawRect(QRect(box.x, box.y, box.width, box.height)); // 绘制矩形


        std::string classString = detection.className;


        QString text = QString::fromStdString(classString);
        DetBox detBox(QRect(box.x, box.y, box.width, box.height), text, {color[2], color[1], color[0]});
        m_detSet.append(detBox);
        printf("det obj:%s\n",qUtf8Printable(text));
        // 使用QFontMetrics测量文本大小
        QFontMetrics metrics(font);
        int textWidth = metrics.horizontalAdvance(text); // 获取文本的宽度
        int textHeight = metrics.height(); // 获取文本的高度

        //            painter.drawText(rect, Qt::AlignLeft | Qt::AlignTop, text);
        // 设置画笔颜色和画刷
        // 设置透明度为127的白色（透明度范围是0到255，0完全透明，255完全不透明）
        QColor semiTransparentColor(255, 255, 255, 80);

        // 设置画笔：透明的白色，无边框
        QPen pen(semiTransparentColor);
        painter.setPen(pen);

        // 设置画刷：同样是透明的白色
        QBrush brush(semiTransparentColor);
        painter.setBrush(brush);

        // 绘制矩形，这里的参数是矩形的位置和大小
        painter.drawRect(QRect(box.x, box.y, textWidth, textHeight));

        painter.setPen(Qt::black); // 设置画笔颜色为黑色
        painter.drawText(box.x,box.y-5+textHeight, text);

        QVector<float> poslist;
        QVector<float> widthlist;
        float step = textWidth/text.length()/2;
        //        printf("text size:%d,%d, width:%d, height:%d, ascent:%d\n",text.length(),classString.length(),textWidth, textHeight, metrics.ascent());
        for(int i=0;i<text.length();i++)
        {
            poslist.append(step*(2*i));
            widthlist.append(step*2);
        }
        results.append(TextBox(QRect(QPoint(box.x,box.y), QPoint(box.x+textWidth, box.y+textHeight)),text, poslist , widthlist));
    }
    // 完成绘图
    painter.end();
    qSort(results.begin(),results.end(),[](const TextBox &a, const TextBox &b){
        return a.rect.bottom() < b.rect.bottom();
    });
    //    //process
    //    processOcrResults(results);

    //    m_isRunning = false;
    //    ocrLock = false;
}

void OCRThread::ocrImage()
{
    while (ocrLock) {
        msleep(200);
        if(m_isCancel)
        {
#ifdef QT_DEBUG
            printf("OCRThread Canceled: %d\n", m_ocrId);
#endif
            return;
        }
    }
    ocrLock = true;
    m_isRunning = true;
    m_isPixmap = false;
    if(m_srcPath.isNull() || m_srcPath.isEmpty())
    {
        m_isPixmap = true;
    }
    //load cache
    QString key;
    key = loadCacheIfPossible();
    CHECK_CANCEL
            ;
    QList<TextBox> results;
    //ocr
    std::vector<OCRPredictResult> ocr_result = ocrTask();
    CHECK_CANCEL
            ;
    if(ocr_result.size())
    {
        //preprocess
        preProcessOcrResults(results, ocr_result);
        CHECK_CANCEL
                ;
        //给背景蒙版，给文本高亮
        highlightText(results);
    }

    objDetAndClas(results);
    CHECK_CANCEL
//            ;
    //process
    processOcrResults(results);
    //saveFile
    saveCacheOrIndex(key);
    m_isRunning = false;
    ocrLock = false;

}


